/*
  ==============================================================================

    PluginChain.cpp
    Created: 28 Nov 2024 11:43:12am
    Author:  leis

  ==============================================================================
*/

#include "PluginChain.h"
#include "PluginChainPool.h"
PluginChain::PluginChain(int sampleRate, int bufferSize, bool realTime)
{
    std::vector<PluginInfo>& pluginInfos = PluginInfos::getInstance().GetPlugins();
    for (int i = 0; i < pluginInfos.size(); i++)
    {
        PluginInfo& pluginInfo = pluginInfos[i];
        if (pluginInfo.vstFomart == nullptr)
        {
            std::cout << "Failed to create plugin format: " + juce::String(pluginInfo.desc.name) << std::endl;
            continue;
        }

        std::unique_ptr<juce::AudioPluginInstance> pluginInstance = pluginInfo.vstFomart->createInstanceFromDescription(pluginInfo.desc, sampleRate, bufferSize);
        if (pluginInstance == nullptr)
        {
            std::cout << "Failed to create plugin instance: " + juce::String(pluginInfo.desc.name) <<
                std::endl;
            continue;
        }
        pluginInstance->setStateInformation(pluginInfo.presetData.getData(), static_cast<int>(pluginInfo.presetData.getSize()));
        /*std::cout << "plugin name:" << pluginInfo.desc.name << std::endl;
        auto params=pluginInstance->getParameters();
        for each(auto param in params)
        {
            std::cout << "param name:" << param->getName(20) << " value:" << param->getValue() << " text:" << param->getCurrentValueAsText() << std::endl;
        }*/
        pluginInstance->setNonRealtime(!realTime);
		this->bufferSize = bufferSize;
        if (realTime)
        {
            pluginInstance->prepareToPlay(sampleRate, bufferSize);
            auto editor = pluginInstance->createEditor();
            editor->addToDesktop(0);
        }
        if (pluginInstance->getTotalNumOutputChannels() > 1)
        {
            maxChannel = 2;
        }
        plugins.push_back(std::move(pluginInstance));
    }
}
PluginChain::~PluginChain()
{
    for (auto& plugin : plugins)
    {
        if (plugin->getActiveEditor() != nullptr)
        {
            plugin->getActiveEditor()->removeFromDesktop();
            delete plugin->getActiveEditor();
        }
    }
    plugins.clear();
}
void PluginChain::Progress(Task* task, std::unique_ptr<juce::AudioFormatReader> reader)
{
    std::cout << "Process file " + juce::String(task->inputPath) << " by chain index " << index << std::endl;
    //std::cout << "Process is this message thread " << juce::MessageManager::getInstance()->isThisTheMessageThread() << std::endl;
    // read audio file
    juce::String inputFilePath(task->inputPath);
    juce::File inputFile(inputFilePath);

    // 输入音频的采样点数
    int originalNumSamples = static_cast<int>(reader->lengthInSamples);
    int sampleRate = static_cast<int>(reader->sampleRate);

    juce::AudioBuffer<float> buffer(reader->numChannels, originalNumSamples);

    // Read the original audio data into the buffer
    reader->read(&buffer, 0, originalNumSamples, 0, true, true);
    // Resample the buffer if task.speed is not 1.0
    int resampledNumSamples = originalNumSamples;
    // 如果变速进行重采样
    if (task->speed != 1.0f)
    {
        resampledNumSamples = static_cast<int>(originalNumSamples / task->speed);
        juce::AudioBuffer<float> resampledBuffer(reader->numChannels, resampledNumSamples);

        for (int channel = 0; channel < reader->numChannels; ++channel)
        {
            const float* sourceData = buffer.getReadPointer(channel);
            float* destData = resampledBuffer.getWritePointer(channel);

            for (int i = 0; i < resampledNumSamples; ++i)
            {
                float sourceIndex = i * task->speed;
                int index0 = static_cast<int>(sourceIndex);
                int index1 = juce::jmin(index0 + 1, originalNumSamples - 1);
                float frac = sourceIndex - index0;
                destData[i] = (1.0f - frac) * sourceData[index0] + frac * sourceData[index1];
            }
        }

        buffer = std::move(resampledBuffer);
    }
	// 开头增加的采样点数。增加1秒的head是为了避免插件还没准备好就开始处理音频数据
    int headPaddingSamples = sampleRate; // 1 second of padding
	// 结尾增加的采样点数。防止效果器制造的尾音被截断
    int tailPaddingSamples = static_cast<int>(task->extendTail * sampleRate / task->speed);
	// 重采样后的采样点数加上头尾增加的采样点数
    int extendedNumSamples = resampledNumSamples + headPaddingSamples + tailPaddingSamples;
	// 输出音频的结尾增加的采样点数
    int outputTailPaddingSamples = tailPaddingSamples;
	// 输出音频的采样点数
    int outputExtendedNumSamples = resampledNumSamples + outputTailPaddingSamples;

    //std::cout << "this file channel num:" << numChannels << std::endl;
    juce::AudioBuffer<float> extendedBuffer(2, extendedNumSamples);
    juce::AudioBuffer<float> outputBuffer(2, outputExtendedNumSamples);

    // 始终使用2个声道进行处理，单声道进行复制。避免效果器找不到第2个声道报错
    // Copy the resampled audio data into the middle of the extended buffer
    extendedBuffer.copyFrom(0, headPaddingSamples, buffer, 0, 0, resampledNumSamples);
    if (buffer.getNumChannels() > 1)
    {
        extendedBuffer.copyFrom(1, headPaddingSamples, buffer, 1, 0, resampledNumSamples);
    }
    else
    {
        extendedBuffer.copyFrom(1, headPaddingSamples, buffer, 0, 0, resampledNumSamples);
    }

    // Clear the padding at the beginningºÍend
    extendedBuffer.clear(0, 0, headPaddingSamples);
    extendedBuffer.clear(0, resampledNumSamples + headPaddingSamples, tailPaddingSamples);
    extendedBuffer.clear(1, 0, headPaddingSamples);
    extendedBuffer.clear(1, resampledNumSamples + headPaddingSamples, tailPaddingSamples);
    outputBuffer.clear(0, 0, outputBuffer.getNumSamples());
    outputBuffer.clear(1, 0, outputBuffer.getNumSamples());

    // process audio
    int totalSamples = extendedBuffer.getNumSamples();
    int chunkSize = bufferSize;
    for (int startSample = 0; startSample < totalSamples; startSample += chunkSize)
    {
        int numSamplesInChunk = juce::jmin(chunkSize, totalSamples - startSample);
		// 当前块复制给chunkBuffer来处理，处理完把结果复制回extendedBuffer
        juce::AudioBuffer<float> chunkBuffer(2, numSamplesInChunk);

        for (int channel = 0; channel < 2; ++channel)
        {
            chunkBuffer.copyFrom(channel, 0, extendedBuffer, channel, startSample, numSamplesInChunk);
        }
        for (auto& plugin : plugins)
        {
            plugin->processBlock(chunkBuffer, juce::MidiBuffer());
			// 如果插件只有一个输出声道，复制第0声道到第1声道
            if (plugin->getMainBusNumOutputChannels() == 1)
            {
                //copy chunkBuffer 0 channel to 1 channel
                chunkBuffer.copyFrom(1, 0, chunkBuffer, 0, 0, numSamplesInChunk);
            }
        }
        for (int channel = 0; channel < 2; ++channel)
        {
            extendedBuffer.copyFrom(channel, startSample, chunkBuffer, channel, 0, numSamplesInChunk);
        }
    }

    // copy extendedBuffer to outputBuffer from extendedBuffer's 1s to end
    for (int channel = 0; channel < 2; ++channel)
    {
        //std::cout << channel << std::endl;
        int num = outputBuffer.getNumSamples();
        if (num > (extendedBuffer.getNumSamples() - headPaddingSamples))
        {
            num = extendedBuffer.getNumSamples() - headPaddingSamples;
        }
        outputBuffer.copyFrom(channel, 0, extendedBuffer, channel, headPaddingSamples, num);
    }

    juce::String outputFilePath(task->outputPath);
    juce::File outputFile(outputFilePath);
    if (outputFile.exists())
    {
        outputFile.deleteFile();
    }
    outputFile.create();
    std::unique_ptr<juce::FileOutputStream> outputStream(outputFile.createOutputStream());
    if (outputStream.get() == nullptr)
    {
        if (task->cb)
        {
            juce::String errorMessage = "Failed to open output file: " + juce::String(task->outputPath);
            task->cb(errorMessage.toRawUTF8());
            std::cout << "Failed to open output file: " + juce::String(task->outputPath) << " by index " << index << std::endl;
        }
        Reset();
        delete task;
        task = nullptr;
        return;
    }

    std::unique_ptr<juce::AudioFormatWriter> writer(task->format
        ->createWriterFor(outputStream.get(), reader->sampleRate, buffer.getNumChannels(), reader->bitsPerSample, {}, 0));
    outputStream.release();
    writer->writeFromAudioSampleBuffer(outputBuffer, 0, outputBuffer.getNumSamples());
    if (task->cb)
    {
        task->cb("success");
    }
    Reset();
    std::cout << "success " + juce::String(task->outputPath) << " by chain index " << index << std::endl;
    delete task;
    task = nullptr;
}
void PluginChain::ProgressRealTime(float* buffer, int bufferSize, int channelNum)
{
    // 创建一个 AudioBuffer 对象
    juce::AudioBuffer<float> audioBuffer(channelNum, bufferSize);

    // 将输入的 buffer 数据复制到 AudioBuffer 中
    for (int channel = 0; channel < channelNum; ++channel)
    {
        float* channelData = audioBuffer.getWritePointer(channel);
        const float* sourceData = buffer + channel * bufferSize;

        std::memcpy(channelData, sourceData, bufferSize * sizeof(float));
    }

    // 处理音频数据
    for (auto& plugin : plugins)
    {
        plugin->processBlock(audioBuffer, juce::MidiBuffer());
    }

    // 将处理后的数据复制回输入的 buffer
    for (int channel = 0; channel < channelNum; ++channel)
    {
        const float* channelData = audioBuffer.getReadPointer(channel);
        float* destData = buffer + channel * bufferSize;

        std::memcpy(destData, channelData, bufferSize * sizeof(float));
    }
}
void PluginChain::Reset()
{
    for (auto& plugin : plugins)
    {
        plugin->reset();
    }
    PluginChainPool::getInstance().MarkChainAsAvailable(index);
}
void PluginChain::Prepare(Task* task)
{
    InnerPrepare(task);
}
void PluginChain::InnerPrepare(Task* task)
{
    //std::cout << "Prepare is this message thread " << juce::MessageManager::getInstance()->isThisTheMessageThread() << std::endl;
    juce::String inputFilePath(task->inputPath);
    juce::File inputFile(inputFilePath);
    std::unique_ptr<juce::AudioFormatReader> reader(
        TaskManager::getInstance().GetFormatManager().createReaderFor(inputFile));
    if (reader.get() == nullptr)
    {
        if (task->cb)
        {
            juce::String errorMessage = "Failed to open input file: " + juce::String(task->inputPath);
            task->cb(errorMessage.toRawUTF8());
            std::cout << "Failed to open input file: " + juce::String(task->inputPath) << index << std::endl;
        }
        Reset();
        delete task;
        task = nullptr;
        return;
    }
    int sampleRate = static_cast<int>(reader->sampleRate);
	// 因为这是一个静态库程序，没有一个持续的主线程，没法用callAsync，只能在prepare前临时设置为消息线程
	// 不会有多个消息线程同时存在，所以不会有线程安全问题
    juce::MessageManager::getInstance()->setCurrentThreadAsMessageThread();
    for each(auto & plugin in plugins)
    {
        plugin->prepareToPlay(sampleRate, bufferSize);
    }
    //this->Progress(task, std::move(reader));
    std::thread([this, task, reader = std::move(reader)]()mutable {
        this->Progress(task, std::move(reader));
        }).detach();
}